package com.token.sysflow.core;

import com.alibaba.fastjson.JSONObject;
import com.token.base.Pl;
import com.token.sysflow.bean.FSysApproveProcessT;
import com.token.sysflow.bean.FSysFlowApproveLog;
import com.token.sysflow.enumerate.ApprovalTypeEnum;
import com.token.sysflow.enumerate.FlowStatusEnum;
import com.token.sysflow.enumerate.FlowTypeEnum;
import com.token.sysflow.enumerate.StepTypeEnum;
import com.token.sysflow.exception.ErrorCodeEnum;

import com.token.sysflow.exception.FlowException;
import com.token.sysflow.hander.*;
import com.token.sql.IntegrateSql;
import com.token.sysflow.impl.AbstractFlowHandlerImpl;

import lombok.Getter;
import lombok.Setter;

import java.util.HashMap;
import java.util.List;

/**
 * 流转引擎处理
 *
 * @author chenjianxiong
 */
public abstract class AbstractFlowCore {
    @Getter
    protected ApproveProcess approveProcess;

    /**
     * 起草人
     */
    @Getter
    protected Person createPerson;

    /**
     * 申请人
     */
    @Getter
    protected Person applyerPerson;

    /**
     * 当前人
     */
    @Getter
    protected Person curPerson;

    @Getter
    protected FlowRecord flowRecord;

    @Getter
    protected AppInfo appInfo;

    @Setter
    @Getter
    protected Flow toFlow;

    @Setter
    @Getter
    protected Flow fromFlow;

    @Getter
    @Setter
    protected Step fromStep;

    @Getter
    @Setter
    protected Step toStep;

    @Getter
    private final IntegrateSql iSql;

    /**
     * 用来记录用户信息
     */
    @Getter
    private final HashMap<String, Object> userInfoMap;

    /**
     * 构造函数
     *
     * @param iSql    类库
     * @param keyUnid 单据关联Id
     * @throws Exception 抛出错误
     */
    public AbstractFlowCore(IntegrateSql iSql, String keyUnid) throws Exception {
        this.iSql = iSql;
        approveProcess = new ApproveProcess(iSql, keyUnid);
        userInfoMap = new HashMap<>();
        initPublic(keyUnid);
    }

    /**
     * @param iSql    类库
     * @param process 审批类
     * @throws Exception 抛出错误
     */
    public AbstractFlowCore(IntegrateSql iSql, ApproveProcess process) throws Exception {
        this.iSql = iSql;
        userInfoMap = new HashMap<>();
        approveProcess = process;
        initPublic(approveProcess.getKeyUnid());

    }

    /**
     * @param iSql 类库
     */
    public AbstractFlowCore(IntegrateSql iSql) {
        this.iSql = iSql;
        userInfoMap = new HashMap<>();
    }

    /**
     * @throws Exception
     */
    public void initPublic(String keyUnid) throws Exception {
        flowRecord = new FlowRecord(iSql, keyUnid);

        if (!approveProcess.isInitRs()) {
            throw new FlowException(ErrorCodeEnum.KEYUNID_NOFIND_ERROR, keyUnid);
        }

        fromFlow = new Flow(iSql, approveProcess.getFlowId());
        if (!fromFlow.isHas()) {
            throw new FlowException(ErrorCodeEnum.FORMFLOW_NOFIND_ERROR, approveProcess.getFlowId());
        }

        this.fromStep = new Step(iSql, approveProcess.getStepId());
        this.appInfo = new AppInfo(iSql, approveProcess.getAppKeyId());

        //判断应用配置是否存在
        if (!appInfo.isHas()) {
            throw new FlowException(ErrorCodeEnum.APP_NOFIND_ERROR, approveProcess.getAppKeyId());
        }

        if (!fromStep.isHas()) {
            throw new FlowException(ErrorCodeEnum.FORMSTEP_NOFIND_ERROR, approveProcess.getStepId());
        }
    }

/*    public void initPublic_async(String keyUnid) throws Exception {
        flowRecord = new FlowRecord(iSql, keyUnid);

        if (!approveProcess.isInitRs()) {
            throw new FlowException(ErrorCodeEnum.KEYUNID_NOFIND_ERROR);
        }

        fromFlow = new Flow(iSql, approveProcess.getFlowId());
        if (!fromFlow.isHas()) {
            throw new FlowException(ErrorCodeEnum.FORMFLOW_NOFIND_ERROR);
        }

        //环节
        CompletableFuture<Step> cfStep = CompletableFuture.supplyAsync(() -> {
            try {
                return new Step(iSql, approveProcess.getStepId());
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        });

        //应用配置
        CompletableFuture<AppInfo> cfAppInfo = CompletableFuture.supplyAsync(() -> {
            try {
                return new AppInfo(iSql, approveProcess.getAppKeyId());
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        });


        this.fromStep = cfStep.get();
        this.appInfo = cfAppInfo.get();

        //判断应用配置是否存在
        if (!appInfo.isHas()) {
            throw new FlowException(ErrorCodeEnum.APP_NOFIND_ERROR);
        }

        if (!fromStep.isHas()) {
            throw new FlowException(ErrorCodeEnum.FORMSTEP_NOFIND_ERROR);
        }
    }*/

    /**
     * @throws Exception
     */
    public void initUserInfo(String creater, String applyer, String curUser) throws Exception {

        this.curPerson = new Person(iSql, Pl.getSysId(), curUser);
        if (!this.curPerson.isInitRs()) {
            throw new FlowException(ErrorCodeEnum.CURUSER_NOFIND);
        }
        userInfoMap.put("CurUser", curPerson.getPersonNumber());
        userInfoMap.put("CurUserDepartmentId", curPerson.getDeptID());
        userInfoMap.put("CurUserCompanyId", curPerson.getComID());

        this.createPerson = new Person(iSql, Pl.getSysId(), creater);
        if (!this.createPerson.isInitRs()) {
            throw new FlowException(ErrorCodeEnum.CREATE_ERROR);
        }

        if (creater.equals(applyer)) {
            userInfoMap.put("Creater", createPerson.getPersonNumber());
            userInfoMap.put("CreateDepartmentId", createPerson.getDeptID());
            userInfoMap.put("CreateCompanyId", createPerson.getComID());
            userInfoMap.put("Applyer", createPerson.getPersonNumber());
            userInfoMap.put("ApplyerDepartmentId", createPerson.getDeptID());
            userInfoMap.put("ApplyerCompanyId", createPerson.getComID());
            applyerPerson = createPerson;
        } else {
            this.createPerson = new Person(iSql, Pl.getSysId(), creater);
            if (!this.createPerson.isInitRs()) {
                throw new FlowException(ErrorCodeEnum.CREATE_ERROR);
            }
            userInfoMap.put("Creater", createPerson.getPersonNumber());
            userInfoMap.put("CreateDepartmentId", createPerson.getDeptID());
            userInfoMap.put("CreateCompanyId", createPerson.getComID());


            this.applyerPerson = new Person(iSql, Pl.getSysId(), applyer);
            if (!this.applyerPerson.isInitRs()) {
                throw new FlowException(ErrorCodeEnum.APPLYER_NOFIND_ERROR);
            }
            userInfoMap.put("Applyer", applyerPerson.getPersonNumber());
            userInfoMap.put("ApplyerDepartmentId", applyerPerson.getDeptID());
            userInfoMap.put("ApplyerCompanyId", applyerPerson.getComID());
        }
    }
/*
    public void initUserInfo_async(String creater, String applyer, String curUser) throws Exception {
        //起草人
        CompletableFuture<Person> cfCreater = null;
        CompletableFuture<Person> cfApplyer = null;
        CompletableFuture<Person> cfCurUser = null;

        if (creater != null) {
            cfCreater = CompletableFuture.supplyAsync(() -> {
                try {
                    return new Person(iSql, Pl.getSysId(), creater);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            });
        }
        //申请人
        if (applyer != null) {
            cfApplyer = CompletableFuture.supplyAsync(() -> {
                try {
                    return new Person(iSql, Pl.getSysId(), applyer);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            });
        }
        //当前人
        if (curUser != null) {
            cfCurUser = CompletableFuture.supplyAsync(() -> {
                try {
                    return new Person(iSql, Pl.getSysId(), curUser);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            });
        }

        if (cfCreater != null) {
            this.createPerson = cfCreater.get();
            if (!this.createPerson.isInitRs()) {
                throw new FlowException(ErrorCodeEnum.CREATE_ERROR);
            }
            userInfoMap.put("Creater", createPerson.getPersonNumber());
            userInfoMap.put("CreateDepartmentId", createPerson.getDeptID());
            userInfoMap.put("CreateCompanyId", createPerson.getComID());
        }

        if (cfApplyer != null) {
            this.applyerPerson = cfApplyer.get();
            if (!this.applyerPerson.isInitRs()) {
                throw new FlowException(ErrorCodeEnum.APPLYER_NOFIND_ERROR);
            }
            userInfoMap.put("Applyer", applyerPerson.getPersonNumber());
            userInfoMap.put("ApplyerDepartmentId", applyerPerson.getDeptID());
            userInfoMap.put("ApplyerCompanyId", applyerPerson.getComID());
        }

        if (cfCurUser != null) {
            this.curPerson = cfCurUser.get();
            if (!this.curPerson.isInitRs()) {
                throw new FlowException(ErrorCodeEnum.CURUSER_NOFIND);
            }
            userInfoMap.put("CurUser", curPerson.getPersonNumber());
            userInfoMap.put("CurUserDepartmentId", curPerson.getDeptID());
            userInfoMap.put("CurUserCompanyId", curPerson.getComID());
        }
    }*/

    /**
     * 设置目标环节
     *
     * @param stepId 环节Id
     * @throws Exception 抛出错误
     */
    protected void setToStepById(String stepId) throws Exception {
        toStep = new Step(iSql, stepId);
        if (!toStep.isHas()) {
            throw new FlowException(ErrorCodeEnum.TOSTEP_NOFIND_ERROR);
        }
    }

    /**
     * 设置目标流程
     *
     * @param flowId 流程Id
     * @throws Exception 抛出错误
     */
    protected void setToFlowById(String flowId) throws Exception {
        if (flowId.equalsIgnoreCase(fromFlow.getFlowId())) {
            toFlow = fromFlow;
        } else {
            toFlow = new Flow(iSql, flowId);
            if (!toFlow.isHas()) {
                throw new FlowException(ErrorCodeEnum.TOFLOW_NOFIND_ERROR, flowId);
            }
        }
    }

    /**
     * @param flowId 流程Id
     * @throws Exception 抛出错误
     */
    protected void setFromFlowById(String flowId) throws Exception {
        fromFlow = new Flow(iSql, flowId);
        if (!fromFlow.isHas()) {
            throw new FlowException(ErrorCodeEnum.FORMFLOW_NOFIND_ERROR, flowId);
        }

    }

    /**
     * @param stepId 环节Id
     * @throws Exception 抛出错误
     */
    protected void setFromStepById(String stepId) throws Exception {
        fromStep = new Step(iSql, stepId);
        if (!fromStep.isHas()) {
            throw new FlowException(ErrorCodeEnum.FORMSTEP_NOFIND_ERROR, stepId);
        }

    }

    /**
     * @param appKeyId
     * @throws Exception
     */
    protected void setAppInfoByAppKeyId(String appKeyId) throws Exception {
        appInfo = new AppInfo(this.getISql(), appKeyId);

        //判断应用配置是否存在
        if (!appInfo.isHas()) {
            throw new FlowException(ErrorCodeEnum.APP_NOFIND_ERROR);
        }

    }

    /**
     * 检查流程状态，结束和终止不允许操作
     * @throws Exception
     */
    protected void checkFlowStatus() throws Exception{
        int status = approveProcess.getFlowStatus();
        if (status == FlowStatusEnum.END.getId()) {
            throw new FlowException(ErrorCodeEnum.FLOWSTATUS_END_ERROR);
        }

        if (status == FlowStatusEnum.STOP.getId()) {
            throw new FlowException(ErrorCodeEnum.FLOWSTATUS_OVER_ERROR);
        }
    }

    /**
     * 创建流转记录
     *
     * @throws Exception 抛出错误
     */
    public void createFlowApproveLog(AbstractFlowHandlerImpl handler, ApproveProcess approveProcess) throws
            Exception {
        FSysFlowApproveLog dao = new FSysFlowApproveLog(iSql);
        dao.setInsert();
        dao.setSysId(approveProcess.getSysId()); //租户ID
        dao.setKeyUnid(approveProcess.getKeyUnid()); //单据关联
        dao.setActionType(handler.getActionType().getId()); //操作类型
        dao.setActionTypeTitle(handler.getActionType().getName());
        FlowTypeEnum flowTypeEnum = FlowTypeEnum.getFlowTypeByValue(approveProcess.getFlowType());

        if (toStep != null) {
            dao.setApprovDesc(fromStep.getStepName() + "-->" + toStep.getStepName()); //动作描述
            System.out.println(flowTypeEnum.getName() + "动作：" + fromStep.getStepName() + "-->" + toStep.getStepName());
            dao.setToFlowName(toFlow.getFlowName() + " v" + toFlow.getFlowVersion());
            dao.setToStepName(toStep.getStepName());
            dao.setToFlowId(toFlow.getFlowId()); //目标流程
            dao.setToStepId(toStep.getStepId()); //目标环节
        } else {
            dao.setApprovDesc(fromStep.getStepName()); //动作描述
            System.out.println(flowTypeEnum.getName() + "动作：" + fromStep.getStepName());
            dao.setToFlowName(fromFlow.getFlowName() + " v" + fromFlow.getFlowVersion());
            dao.setToStepName(fromStep.getStepName());
            dao.setToFlowId(fromFlow.getFlowId()); //目标流程
            dao.setToStepId(fromStep.getStepId()); //目标环节
        }
        dao.setApprover(handler.getActionUser()); //办理人账号ID
        dao.setStartTime(approveProcess.getSysEditTime()); //开始办理时间
        dao.setEndTime(Pl.getCurDataTime()); //结束办理时间
        dao.setToDoTime(fromStep.getStdHours()); //标准办理时间(h)
        dao.setLossTime(0); //消耗时间(分钟)
        dao.setOptions(handler.getOpinions()); //办理意见
        dao.setStatus(0); //状态
        dao.setFormFlowId(fromFlow.getFlowId()); //当前流程
        dao.setFormStepId(fromStep.getStepId()); //当前环节
        dao.setApproverName(curPerson.getPersonName());
        dao.setFormFlowName(fromFlow.getFlowName() + " v" + fromFlow.getFlowVersion());
        dao.setFormStepName(fromStep.getStepName());
        dao.executeUpdate();
        dao.close();

        // TODO 消耗时间没完成
    }

    /**
     * @param handler
     * @throws Exception
     */
    public void createFlowApproveLog(AbstractFlowHandlerImpl handler) throws Exception {
        this.createFlowApproveLog(handler, approveProcess);
    }

    /**
     * 更新审批数据
     *
     * @param flowHandler    参数类
     * @param flowStatusEnum 流程状态
     * @throws Exception 抛出错误
     */
    public void updateApproveProcess(AbstractFlowHandlerImpl flowHandler, FlowStatusEnum flowStatusEnum) throws
            Exception {
        approveProcess.setUpdateCur();
        approveProcess.setFlowStatus(flowStatusEnum.getId());
        approveProcess.setFlowId(toFlow.getFlowId());
        approveProcess.setStepId(toStep.getStepId());
        approveProcess.setPreFlowId(fromFlow.getFlowId());
        approveProcess.setPreStepId(fromStep.getStepId());
        approveProcess.setPrevApprover(flowHandler.getActionUser());
        approveProcess.setSysEditTime(flowHandler.getActionTimer());
        approveProcess.setApprovalType(StepTypeEnum.getStepTypeByValue(toStep.getStepType()).getApprovalType());
        approveProcess.setSysEditUser(flowHandler.getActionUser());
        approveProcess.setIsReturned(approveProcess.isReturnAction() ? 1 : 0);

        if (toStep.getStepType().equals(StepTypeEnum.STEPTYPE_TASK_HS.getTypeKey())) { // 会审环节
            approveProcess.setHSWay(toStep.getStepHSAllWay()); //写入会审条件
        } else {
            approveProcess.setHSWay("");
        }
        approveProcess.executeUpdate();

        FSysApproveProcessT processT = new FSysApproveProcessT(this.getISql(), approveProcess.getKeyUnid());
        if (processT.isInitRs()) {//子流程，没有FSysApproveProcessT对象
            processT.setUpdateCur();
            processT.setKeyUnid(approveProcess.getKeyUnid()); //单据关联ID
            processT.setFlowTitle(toFlow.getFlowName() + " v" + toFlow.getFlowVersion()); //当前流程
            processT.setStepTitle(toStep.getStepName()); //当前环节
            processT.setPreFlowTitle(fromFlow.getFlowName() + " v" + toFlow.getFlowVersion()); //上一环节流程
            processT.setPreStepTitle(fromStep.getStepName()); //上一环节环节
            processT.setApprovalTypeTitle(ApprovalTypeEnum.SP.getName()); //审批类型（普通审批，会审，会签，主-子流程，组织会审，组织会签）
            processT.setMessage("");
            processT.setCode(0);
            processT.executeUpdate();
            processT.close();
        }
        //删除目标环节的办理人。一般情况下不用，退回再提交的情况就需要删除，可优化
        approveProcess.delStepApproving(toStep);
    }

    /**
     * @param code
     * @param message
     * @throws Exception
     */
    public String updateApproveProcessErr(int code, String message) {
        if (approveProcess == null) {
            return "";
        }
        try {
            FSysApproveProcessT processT = new FSysApproveProcessT(this.getISql(), approveProcess.getKeyUnid());
            if (!processT.getKeyId().equals("")) {
                processT.setUpdateCur();
                processT.setCode(code);
                processT.setMessage(message);
                processT.executeUpdate();
            }
            processT.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return approveProcess.getFlowNo();//用于回写提交日志。
    }

    /**
     * 组织会签、组织会审更新
     *
     * @param flowHandler
     * @param typeEnum
     * @throws Exception
     */
    public void updateApproveProcess(AbstractFlowHandlerImpl flowHandler, ApprovalTypeEnum typeEnum) throws
            Exception {
        approveProcess.setUpdateCur();
        approveProcess.setFlowStatus(FlowStatusEnum.ING.getId());
        approveProcess.setSysEditTime(flowHandler.getActionTimer());
        approveProcess.setPrevApprover(flowHandler.getActionUser());
        approveProcess.setApprovalType(typeEnum.getId());
        approveProcess.setSysEditUser(flowHandler.getActionUser());
        approveProcess.setIsReturned(approveProcess.isReturnAction() ? 1 : 0);
        approveProcess.executeUpdate();

        FSysApproveProcessT processT = new FSysApproveProcessT(this.getISql(), approveProcess.getKeyUnid());
        processT.setUpdateCur();
        processT.setKeyUnid(approveProcess.getKeyUnid()); //单据关联ID
        processT.setApprovalTypeTitle(typeEnum.getName()); //审批类型（普通审批，会审，会签，主-子流程，组织会审，组织会签）
        processT.setCode(ErrorCodeEnum.SUCCESS.getCode());
        processT.setMessage(ErrorCodeEnum.SUCCESS.getDesc());
        processT.executeUpdate();
        processT.close();
    }


    /**
     * 更新审批数据 审批中
     *
     * @param flowHandler 参数类
     * @throws Exception 抛出错误
     */
    public void updateApproveProcess(AbstractFlowHandlerImpl flowHandler) throws Exception {
        this.updateApproveProcess(flowHandler, FlowStatusEnum.ING);
    }

    /**
     * 执行接口
     */
    public abstract void execute(AbstractFlowHandlerImpl baseFlowHandler) throws Exception;

    /**
     * 关闭
     */
    public void close() {
        if (approveProcess != null) approveProcess.close();
        if (toFlow != null) toFlow.close();
        if (toStep != null) toStep.close();
        if (applyerPerson != null) applyerPerson.close();
        if (createPerson != null) createPerson.close();
        if (curPerson != null) curPerson.close();
        if (appInfo != null) appInfo.close();
        if (flowRecord != null) flowRecord.close();
        fromFlow.close();
        fromStep.close();
    }

    /**
     * 处理根据环节配置的知会来处理知会消息
     */
    public void handleNotification(String actionUser, String[] selectApprovers, JSONObject fieldJson) throws Exception {
        if (toStep.getUseNotify().equals("1")) { //知会开关。
            Notify notify = new Notify(this.iSql);
            notify.setSendType(toStep.getNotifyWay());
            notify.setActionUserId(actionUser);
            notify.setStep(toStep);
            notify.insertConfigNotify(approveProcess, selectApprovers, fieldJson);
            notify.close();
        }
    }

    /**
     * 处理系统知会：退回，取回，终止等。
     */
    public void handleSystemNotify(String subject, String message, List<String> sendUsers) throws Exception {
        Notify notify = new Notify(this.iSql);
        notify.setSubject(subject);
        notify.setMessage(message);
        notify.setActionUserId("");
        notify.setSendUsers(sendUsers);
        notify.setStep(toStep);
        notify.insertSystemNotify();
        notify.close();
    }

    /**
     * 处理流程事件
     */
    public void handleFlowEvent() {
        // TODO 处理流程事件
    }

    /**
     * 处理对外接口
     */
    public void handleExternalApi() {
        // TODO 处理对外接口
    }
}